"""
智能问答客服API接口
支持多轮对话、知识库检索、关键词+语义混合检索
"""
import os
import json
import uuid
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Request, status
from pydantic import BaseModel
from starlette.concurrency import run_in_threadpool
from utils import JWTUtils
from utils.redis_utils import get_redis
from utils.rag_service import get_rag_service
from dashscope import Generation



router = APIRouter(prefix="/qa", tags=["智能问答"])

# 获取当前用户ID
def get_current_user_id(request: Request) -> int:
    """从请求头获取当前用户ID"""
    authorization = request.headers.get("Authorization")
    if not authorization:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="未提供认证令牌"
        )

    # 提取 token (格式: "Bearer <token>")
    token = authorization.replace("Bearer ", "").strip()
    # 解码令牌
    payload = JWTUtils.decode_access_token(token)
    if not payload or not payload.get("user_id"):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="认证令牌无效"
        )
    return payload["user_id"]


class QARequest(BaseModel):
    """问答请求"""
    message: str
    user_id: Optional[str] = None
    use_knowledge_base: bool = True  # 是否使用知识库
    use_hybrid_search: bool = True  # 是否使用混合检索
class QAResponse(BaseModel):
    """问答响应"""
    message: str
    user_id: str
    use_knowledge_base: bool
    source: str  # "knowledge_base" 或 "llm_only"
class HistoryResponse(BaseModel):
    """历史记录响应"""
    messages: list
class DialogListResponse(BaseModel):
    """对话列表响应"""
    dialogs: list
class NewDialogResponse(BaseModel):
    """新建对话响应"""
    user_id: str



# 智能问答接口
@router.post("/chat", response_model=QAResponse, summary="智能问答")
async def qa_chat(
    request_data: QARequest,
    request: Request = None
):
    """
    智能问答主接口，处理用户提问并返回AI回答
    支持基于知识库的问答和纯大模型问答两种模式
    """
    # 获取用户ID和问题内容
    user_id = request_data.user_id
    message = request_data.message
    # 验证问题内容是否为空
    if not message:
        raise HTTPException(status_code=400, detail="请输入问题")
    # 如果没有提供user_id，生成一个新的UUID作为用户标识
    if not user_id:
        user_id = str(uuid.uuid4())
    # 获取Redis客户端实例用于存储对话历史
    redis_client = get_redis()
    # 构造Redis键名并获取用户的对话历史
    history_key = f"qa:user:{user_id}"
    history_data = redis_client.get(history_key)
    # 如果存在历史记录则解析，否则初始化为空列表
    messages = json.loads(history_data) if history_data else []
    # 将当前用户问题添加到对话历史中
    messages.append({'role': 'user', 'content': message})
    try:
        # 初始化响应文本和来源标记
        response_text = ""
        source = "llm_only"
        # 判断是否启用知识库功能
        if request_data.use_knowledge_base:
            try:
                # 获取RAG服务实例
                rag_service = get_rag_service()
                
                # 检查知识库是否为空，如果为空则自动构建
                if rag_service.is_knowledge_base_empty():
                    print("检测到知识库为空，开始自动构建...")
                    try:
                        success = await run_in_threadpool(rag_service.build_knowledge_base)
                        if success:
                            print("知识库自动构建成功！")
                        else:
                            print("警告: 知识库自动构建失败，将使用纯LLM模式")
                            response_text = await _call_llm_only(messages)
                            source = "llm_only"
                            messages.append({'role': 'assistant', 'content': response_text})
                            redis_client.setex(history_key, 86400 * 7, json.dumps(messages))
                            return QAResponse(
                                message=response_text,
                                user_id=user_id,
                                use_knowledge_base=request_data.use_knowledge_base,
                                source=source
                            )
                    except Exception as build_error:
                        print(f"知识库自动构建出错: {build_error}")
                        response_text = await _call_llm_only(messages)
                        source = "llm_only"
                        messages.append({'role': 'assistant', 'content': response_text})
                        redis_client.setex(history_key, 86400 * 7, json.dumps(messages))
                        return QAResponse(
                            message=response_text,
                            user_id=user_id,
                            use_knowledge_base=request_data.use_knowledge_base,
                            source=source
                        )

                # 使用完整的14步RAG流程（推荐方式）
                # 在线程池中执行耗时的RAG查询操作，避免阻塞主线程
                result = await run_in_threadpool(
                    rag_service.query,
                    message,
                    request_data.use_hybrid_search,  # 是否使用混合搜索（关键词+语义）
                    True  # return_sources=True 返回检索到的知识源信息
                )
                # 从结果中提取回答文本，默认为空字符串
                response_text = result.get("answer", "")
                # 根据是否有检索到知识源来判断回答来源
                source = "knowledge_base" if result.get("sources") else "llm_only"

            except Exception as e:
                # 知识库检索失败时的异常处理，回退到纯LLM模式
                print(f"知识库检索失败: {e}")
                response_text = await _call_llm_only(messages)
                source = "llm_only"
        else:
            # 不使用知识库，直接调用纯LLM生成回答
            response_text = await _call_llm_only(messages)
            source = "llm_only"

        # 将AI助手的回答添加到对话历史中
        messages.append({'role': 'assistant', 'content': response_text})

        # 将更新后的对话历史保存到Redis中，设置7天过期时间
        redis_client.setex(history_key, 86400 * 7, json.dumps(messages))

        # 构造并返回标准化的响应结构
        return QAResponse(
            message=response_text,
            user_id=user_id,
            use_knowledge_base=request_data.use_knowledge_base,
            source=source
        )

    except Exception as e:
        # 捕获处理过程中的所有异常并返回错误响应
        raise HTTPException(status_code=500, detail=f"处理失败: {str(e)}")


async def _call_llm_only(messages: list) -> str:
    """
    调用纯LLM（不使用知识库）
    直接使用对话历史作为上下文调用通义千问大模型

    Args:
        messages: 对话历史列表，包含用户和助手的消息

    Returns:
        str: 大模型生成的回答文本
    """
    import os

    # 在线程池中异步调用通义千问API
    response = await run_in_threadpool(
        Generation.call,
        api_key=os.getenv("DASHSCOPE_API_KEY", ""),  # 从环境变量获取API密钥
        model="qwen-plus",  # 使用qwen-plus模型
        messages=messages,  # 传递完整的对话历史作为上下文
        result_format="message"  # 指定结果格式
    )

    # 从API响应中提取助手的回答内容
    return response.output.choices[0].message.content



@router.get("/history/{user_id}", response_model=HistoryResponse, summary="获取对话历史")
async def get_history(user_id: str):
    """
    获取用户的对话历史
    """
    redis_client = get_redis()
    history_key = f"qa:user:{user_id}"
    history_data = redis_client.get(history_key)
    
    if not history_data:
        raise HTTPException(status_code=404, detail="用户不存在或无对话历史")
    
    messages = json.loads(history_data)
    return HistoryResponse(messages=messages)


@router.get("/dialogs", response_model=DialogListResponse, summary="获取对话列表")
async def get_dialogs():
    """
    获取所有对话列表
    """
    redis_client = get_redis()
    dialogs = redis_client.keys("qa:user:*")
    
    # 解码字节字符串为普通字符串
    decoded_dialogs = [dialog.decode('utf-8') for dialog in dialogs]
    return DialogListResponse(dialogs=decoded_dialogs)


@router.post("/new-dialog", response_model=NewDialogResponse, summary="新建对话")
async def new_dialog():
    """
    新建一个对话会话
    """
    user_id = str(uuid.uuid4())
    redis_client = get_redis()
    history_key = f"qa:user:{user_id}"
    
    # 创建空的对话历史
    messages = []
    redis_client.setex(history_key, 86400 * 7, json.dumps(messages))
    
    return NewDialogResponse(user_id=user_id)


@router.delete("/history/{user_id}", summary="删除对话历史")
async def delete_history(user_id: str):
    """
    删除用户的对话历史
    """
    redis_client = get_redis()
    history_key = f"qa:user:{user_id}"
    redis_client.delete(history_key)
    
    return {"message": "对话历史已删除"}



@router.post("/build-knowledge-base", summary="构建知识库")
async def build_knowledge_base():
    """
    构建或更新知识库
    """
    try:
        rag_service = get_rag_service()
        success = await run_in_threadpool(rag_service.build_knowledge_base)
        
        if success:
            return {"message": "知识库构建成功"}
        else:
            raise HTTPException(status_code=400, detail="知识库构建失败，请检查知识库目录是否存在文件")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"构建知识库失败: {str(e)}")

